{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "O5pKTpHfeJ0t"
   },
   "source": [
    "# CFO Correction Network\n",
    "\n",
    "This notebooks contains two network architectures to **learn to correct Carrier Frequency Offset (CFO)**, given preamble and preamble convolved data.\n",
    "\n",
    "* **First Approach**: Feedforward network (~36K trainable paramters)\n",
    "    * Pros: Faster inference\n",
    "    * Cons: Fixed Preabmle Length\n",
    "* **Second Approach**: Recurrent Neural Network network (~10K trainable paramters)\n",
    "    * Pros: variable preamble length\n",
    "    * Cons: Slower\n",
    "    \n",
    "## Environment Setup\n",
    "\n",
    "### Required packages\n",
    "* Tensorflow\n",
    "* Keras\n",
    "* Commpy\n",
    "* Pydot\n",
    "*  `graphviz` for visualization. `sudo apt-get install graphviz`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Import packages from other direction. Itis necessary if the project is structured as:\n",
    "# my_project\n",
    "# ├── notebooks\n",
    "# │   └── current_notebook.ipynb\n",
    "# ├── local_python_package\n",
    "# │   ├── __init__.py\n",
    "# │   ├── models.py\n",
    "# ├── README.md\n",
    "import os\n",
    "import sys\n",
    "module_path = os.path.abspath(os.path.join('..'))\n",
    "if module_path not in sys.path:\n",
    "    sys.path.append(module_path)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     },
     "base_uri": "https://localhost:8080/",
     "height": 34
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 1245,
     "status": "ok",
     "timestamp": 1532646976703,
     "user": {
      "displayName": "Dat Nguyen",
      "photoUrl": "//lh3.googleusercontent.com/-irIcNYd-KIw/AAAAAAAAAAI/AAAAAAAAAEs/NlM8kG6RL4Q/s50-c-k-no/photo.jpg",
      "userId": "108917076199533451784"
     },
     "user_tz": 420
    },
    "id": "Ca66rN5HeJ01",
    "outputId": "9d2d668a-4420-4c59-cfe6-fec222a4c932"
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Using TensorFlow backend.\n"
     ]
    }
   ],
   "source": [
    "import multiprocessing as mp\n",
    "import numpy as np\n",
    "import tensorflow as tf\n",
    "from radioml.dataset import RadioDataGenerator\n",
    "from sklearn.metrics import mean_squared_error\n",
    "\n",
    "# For visualization\n",
    "from IPython.display import SVG, display\n",
    "from keras.utils.vis_utils import model_to_dot\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "import pylab"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Define Parameters for this experiment"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "DATA_LEN = 200\n",
    "PREAMBLE_LEN = 40\n",
    "CHANNEL_LEN = 1\n",
    "\n",
    "SNR_TRAIN = 20.0\n",
    "OMEGA_TRAIN = 1/50"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Define Data Generators for both two approaches"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define Radio\n",
    "radio = RadioDataGenerator(DATA_LEN,PREAMBLE_LEN, CHANNEL_LEN, modulation_scheme='QPSK')\n",
    "\n",
    "training_generator   = radio.cfo_data_generator(OMEGA_TRAIN, SNR_TRAIN, \n",
    "                                                batch_size=256, num_cpus=16)\n",
    "\n",
    "validation_generator = radio.cfo_data_generator(OMEGA_TRAIN, SNR_TRAIN, \n",
    "                                                batch_size=256, num_cpus=16, seed=2018)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 720x288 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Visualize a few examples\n",
    "examples = next(radio.cfo_data_generator(OMEGA_TRAIN, SNR_TRAIN, batch_size=4, num_cpus=8))\n",
    "[preambles, preambles_conv], cfo_corrected = examples\n",
    "symbols, groundtruths = np.unique(preambles.view(np.complex),return_inverse=True)\n",
    "\n",
    "_, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4))\n",
    "\n",
    "ax1.scatter(preambles_conv[...,0].flatten(),\n",
    "            preambles_conv[...,1].flatten(), c=groundtruths)\n",
    "ax1.axhline()\n",
    "ax1.axvline()\n",
    "\n",
    "_ = ax1.set_title(\"Preamble_Conv before CFO Correction \")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "hLEGx68_eJ1j"
   },
   "source": [
    "## First Approach: Feedforward Network"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     },
     "base_uri": "https://localhost:8080/",
     "height": 648
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 482,
     "status": "ok",
     "timestamp": 1532646978288,
     "user": {
      "displayName": "Dat Nguyen",
      "photoUrl": "//lh3.googleusercontent.com/-irIcNYd-KIw/AAAAAAAAAAI/AAAAAAAAAEs/NlM8kG6RL4Q/s50-c-k-no/photo.jpg",
      "userId": "108917076199533451784"
     },
     "user_tz": 420
    },
    "id": "mtYdGSnieJ1n",
    "outputId": "906a9d75-754e-4519-bec3-0b879ed8a77a"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Number of training parameters: 36401\n"
     ]
    },
    {
     "data": {
      "image/svg+xml": [
       "<svg height=\"553pt\" viewBox=\"0.00 0.00 647.50 553.00\" width=\"648pt\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
       "<g class=\"graph\" id=\"graph0\" transform=\"scale(1 1) rotate(0) translate(4 549)\">\n",
       "<title>G</title>\n",
       "<polygon fill=\"white\" points=\"-4,4 -4,-549 643.5,-549 643.5,4 -4,4\" stroke=\"none\"/>\n",
       "<!-- 140310255450768 -->\n",
       "<g class=\"node\" id=\"node1\"><title>140310255450768</title>\n",
       "<polygon fill=\"none\" points=\"0,-498.5 0,-544.5 281,-544.5 281,-498.5 0,-498.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"68\" y=\"-517.8\">Preamble: InputLayer</text>\n",
       "<polyline fill=\"none\" points=\"136,-498.5 136,-544.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"163.5\" y=\"-529.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"136,-521.5 191,-521.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"163.5\" y=\"-506.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"191,-498.5 191,-544.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"236\" y=\"-529.3\">(None, 40, 2)</text>\n",
       "<polyline fill=\"none\" points=\"191,-521.5 281,-521.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"236\" y=\"-506.3\">(None, 40, 2)</text>\n",
       "</g>\n",
       "<!-- 140310255450152 -->\n",
       "<g class=\"node\" id=\"node3\"><title>140310255450152</title>\n",
       "<polygon fill=\"none\" points=\"109.5,-415.5 109.5,-461.5 499.5,-461.5 499.5,-415.5 109.5,-415.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"187\" y=\"-434.8\">concatenate: Concatenate</text>\n",
       "<polyline fill=\"none\" points=\"264.5,-415.5 264.5,-461.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"292\" y=\"-446.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"264.5,-438.5 319.5,-438.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"292\" y=\"-423.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"319.5,-415.5 319.5,-461.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"409.5\" y=\"-446.3\">[(None, 40, 2), (None, 40, 2)]</text>\n",
       "<polyline fill=\"none\" points=\"319.5,-438.5 499.5,-438.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"409.5\" y=\"-423.3\">(None, 80, 2)</text>\n",
       "</g>\n",
       "<!-- 140310255450768&#45;&gt;140310255450152 -->\n",
       "<g class=\"edge\" id=\"edge1\"><title>140310255450768-&gt;140310255450152</title>\n",
       "<path d=\"M185.314,-498.366C205.471,-488.41 229.433,-476.576 250.566,-466.138\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"252.324,-469.173 259.74,-461.607 249.224,-462.897 252.324,-469.173\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 140310255449536 -->\n",
       "<g class=\"node\" id=\"node2\"><title>140310255449536</title>\n",
       "<polygon fill=\"none\" points=\"299.5,-498.5 299.5,-544.5 639.5,-544.5 639.5,-498.5 299.5,-498.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"397\" y=\"-517.8\">PreambleConvolved: InputLayer</text>\n",
       "<polyline fill=\"none\" points=\"494.5,-498.5 494.5,-544.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"522\" y=\"-529.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"494.5,-521.5 549.5,-521.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"522\" y=\"-506.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"549.5,-498.5 549.5,-544.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"594.5\" y=\"-529.3\">(None, 40, 2)</text>\n",
       "<polyline fill=\"none\" points=\"549.5,-521.5 639.5,-521.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"594.5\" y=\"-506.3\">(None, 40, 2)</text>\n",
       "</g>\n",
       "<!-- 140310255449536&#45;&gt;140310255450152 -->\n",
       "<g class=\"edge\" id=\"edge2\"><title>140310255449536-&gt;140310255450152</title>\n",
       "<path d=\"M424.413,-498.366C404.132,-488.41 380.025,-476.576 358.763,-466.138\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"360.052,-462.872 349.532,-461.607 356.967,-469.155 360.052,-462.872\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 140310255451552 -->\n",
       "<g class=\"node\" id=\"node4\"><title>140310255451552</title>\n",
       "<polygon fill=\"none\" points=\"182.5,-332.5 182.5,-378.5 426.5,-378.5 426.5,-332.5 182.5,-332.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"232\" y=\"-351.8\">Flatten: Flatten</text>\n",
       "<polyline fill=\"none\" points=\"281.5,-332.5 281.5,-378.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"309\" y=\"-363.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"281.5,-355.5 336.5,-355.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"309\" y=\"-340.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"336.5,-332.5 336.5,-378.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"381.5\" y=\"-363.3\">(None, 80, 2)</text>\n",
       "<polyline fill=\"none\" points=\"336.5,-355.5 426.5,-355.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"381.5\" y=\"-340.3\">(None, 160)</text>\n",
       "</g>\n",
       "<!-- 140310255450152&#45;&gt;140310255451552 -->\n",
       "<g class=\"edge\" id=\"edge3\"><title>140310255450152-&gt;140310255451552</title>\n",
       "<path d=\"M304.5,-415.366C304.5,-407.152 304.5,-397.658 304.5,-388.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"308,-388.607 304.5,-378.607 301,-388.607 308,-388.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 140310255451664 -->\n",
       "<g class=\"node\" id=\"node5\"><title>140310255451664</title>\n",
       "<polygon fill=\"none\" points=\"158,-249.5 158,-295.5 451,-295.5 451,-249.5 158,-249.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"235.5\" y=\"-268.8\">CFONet_dense_1: Dense</text>\n",
       "<polyline fill=\"none\" points=\"313,-249.5 313,-295.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"340.5\" y=\"-280.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"313,-272.5 368,-272.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"340.5\" y=\"-257.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"368,-249.5 368,-295.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"409.5\" y=\"-280.3\">(None, 160)</text>\n",
       "<polyline fill=\"none\" points=\"368,-272.5 451,-272.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"409.5\" y=\"-257.3\">(None, 100)</text>\n",
       "</g>\n",
       "<!-- 140310255451552&#45;&gt;140310255451664 -->\n",
       "<g class=\"edge\" id=\"edge4\"><title>140310255451552-&gt;140310255451664</title>\n",
       "<path d=\"M304.5,-332.366C304.5,-324.152 304.5,-314.658 304.5,-305.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"308,-305.607 304.5,-295.607 301,-305.607 308,-305.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 140310254987472 -->\n",
       "<g class=\"node\" id=\"node6\"><title>140310254987472</title>\n",
       "<polygon fill=\"none\" points=\"158,-166.5 158,-212.5 451,-212.5 451,-166.5 158,-166.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"235.5\" y=\"-185.8\">CFONet_dense_2: Dense</text>\n",
       "<polyline fill=\"none\" points=\"313,-166.5 313,-212.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"340.5\" y=\"-197.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"313,-189.5 368,-189.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"340.5\" y=\"-174.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"368,-166.5 368,-212.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"409.5\" y=\"-197.3\">(None, 100)</text>\n",
       "<polyline fill=\"none\" points=\"368,-189.5 451,-189.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"409.5\" y=\"-174.3\">(None, 100)</text>\n",
       "</g>\n",
       "<!-- 140310255451664&#45;&gt;140310254987472 -->\n",
       "<g class=\"edge\" id=\"edge5\"><title>140310255451664-&gt;140310254987472</title>\n",
       "<path d=\"M304.5,-249.366C304.5,-241.152 304.5,-231.658 304.5,-222.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"308,-222.607 304.5,-212.607 301,-222.607 308,-222.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 140310254988536 -->\n",
       "<g class=\"node\" id=\"node7\"><title>140310254988536</title>\n",
       "<polygon fill=\"none\" points=\"158,-83.5 158,-129.5 451,-129.5 451,-83.5 158,-83.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"235.5\" y=\"-102.8\">CFONet_dense_3: Dense</text>\n",
       "<polyline fill=\"none\" points=\"313,-83.5 313,-129.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"340.5\" y=\"-114.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"313,-106.5 368,-106.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"340.5\" y=\"-91.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"368,-83.5 368,-129.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"409.5\" y=\"-114.3\">(None, 100)</text>\n",
       "<polyline fill=\"none\" points=\"368,-106.5 451,-106.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"409.5\" y=\"-91.3\">(None, 100)</text>\n",
       "</g>\n",
       "<!-- 140310254987472&#45;&gt;140310254988536 -->\n",
       "<g class=\"edge\" id=\"edge6\"><title>140310254987472-&gt;140310254988536</title>\n",
       "<path d=\"M304.5,-166.366C304.5,-158.152 304.5,-148.658 304.5,-139.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"308,-139.607 304.5,-129.607 301,-139.607 308,-139.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 140310255449424 -->\n",
       "<g class=\"node\" id=\"node8\"><title>140310255449424</title>\n",
       "<polygon fill=\"none\" points=\"169.5,-0.5 169.5,-46.5 439.5,-46.5 439.5,-0.5 169.5,-0.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"235.5\" y=\"-19.8\">CFOEstimate: Dense</text>\n",
       "<polyline fill=\"none\" points=\"301.5,-0.5 301.5,-46.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"329\" y=\"-31.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"301.5,-23.5 356.5,-23.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"329\" y=\"-8.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"356.5,-0.5 356.5,-46.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"398\" y=\"-31.3\">(None, 100)</text>\n",
       "<polyline fill=\"none\" points=\"356.5,-23.5 439.5,-23.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"398\" y=\"-8.3\">(None, 1)</text>\n",
       "</g>\n",
       "<!-- 140310254988536&#45;&gt;140310255449424 -->\n",
       "<g class=\"edge\" id=\"edge7\"><title>140310254988536-&gt;140310255449424</title>\n",
       "<path d=\"M304.5,-83.3664C304.5,-75.1516 304.5,-65.6579 304.5,-56.7252\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"308,-56.6068 304.5,-46.6068 301,-56.6069 308,-56.6068\" stroke=\"black\"/>\n",
       "</g>\n",
       "</g>\n",
       "</svg>"
      ],
      "text/plain": [
       "<IPython.core.display.SVG object>"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "tf.keras.backend.clear_session()\n",
    "\n",
    "def cfo_network(preamble, preamble_conv, scope='CFONet'):\n",
    "    \"\"\"\n",
    "    Arguments:\n",
    "        preamble :     tf.Tensor float32 -  [batch, preamble_length, 2]\n",
    "        preamble_conv: tf.Tensor float32 -  [batch, preamble_length, 2]\n",
    "        \n",
    "    Return:\n",
    "        cfo_estimate: tf.Tensor float32 - [batch_size, 1]\n",
    "    \"\"\"\n",
    "    with tf.name_scope(scope):\n",
    "        inputs = tf.keras.layers.concatenate([preamble, preamble_conv], axis=1)\n",
    "        inputs = tf.keras.layers.Flatten(name='Flatten')(inputs)\n",
    "        x = tf.keras.layers.Dense(100, 'tanh', name=scope+\"_dense_1\")(inputs)\n",
    "        x = tf.keras.layers.Dense(100, 'tanh', name=scope+\"_dense_2\")(x)\n",
    "        x = tf.keras.layers.Dense(100, 'tanh', name=scope+\"_dense_3\")(x)\n",
    "    cfo_est = tf.keras.layers.Dense(1, 'linear',name='CFOEstimate')(x)\n",
    "    \n",
    "    return cfo_est\n",
    "\n",
    "# ############################\n",
    "# Construct FeedForward Model\n",
    "# ############################\n",
    "# Think of keras.Input as tf.placeholder\n",
    "preamble       = tf.keras.layers.Input(shape=(40, 2), name='Preamble')\n",
    "preamble_conv  = tf.keras.layers.Input(shape=(40, 2), name='PreambleConvolved')\n",
    "\n",
    "# Build tensorflow graph\n",
    "omega_estimate  = cfo_network(preamble, preamble_conv)\n",
    "\n",
    "# Instead of using TF training API, we convert Graph to tf.keras.Model.\n",
    "model = tf.keras.Model(inputs=[preamble, preamble_conv],\n",
    "                       outputs=omega_estimate)\n",
    "\n",
    "print(\"Number of training parameters: %d\" % model.count_params())\n",
    "tf.keras.utils.plot_model(model, \"model.svg\", show_shapes=True, show_layer_names=True)\n",
    "SVG(\"model.svg\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     },
     "base_uri": "https://localhost:8080/",
     "height": 153
    },
    "colab_type": "code",
    "id": "JZPWrqfOeJ1y",
    "outputId": "dc02a0bc-df04-4108-d3ff-c94c45c3394f"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/50\n",
      "1000/1000 [==============================] - 166s 166ms/step - loss: 0.0027 - val_loss: 3.0882e-04\n",
      "Epoch 2/50\n",
      "1000/1000 [==============================] - 164s 164ms/step - loss: 2.3164e-04 - val_loss: 1.8926e-04\n",
      "Epoch 3/50\n",
      "1000/1000 [==============================] - 169s 169ms/step - loss: 1.4517e-04 - val_loss: 1.1884e-04\n",
      "Epoch 4/50\n",
      "1000/1000 [==============================] - 168s 168ms/step - loss: 7.9307e-05 - val_loss: 7.7286e-05\n",
      "Epoch 5/50\n",
      "1000/1000 [==============================] - 164s 164ms/step - loss: 5.1112e-05 - val_loss: 5.4337e-05\n",
      "Epoch 6/50\n",
      "1000/1000 [==============================] - 164s 164ms/step - loss: 3.5172e-05 - val_loss: 3.5468e-05\n",
      "Epoch 7/50\n",
      "1000/1000 [==============================] - 164s 164ms/step - loss: 2.4023e-05 - val_loss: 1.9056e-05\n",
      "Epoch 8/50\n",
      "1000/1000 [==============================] - 165s 165ms/step - loss: 1.5570e-05 - val_loss: 1.2388e-05\n",
      "Epoch 9/50\n",
      "1000/1000 [==============================] - 164s 164ms/step - loss: 1.1160e-05 - val_loss: 8.9057e-06\n",
      "Epoch 10/50\n",
      "1000/1000 [==============================] - 167s 167ms/step - loss: 8.5792e-06 - val_loss: 5.5313e-06\n",
      "Epoch 11/50\n",
      "1000/1000 [==============================] - 168s 168ms/step - loss: 6.7217e-06 - val_loss: 1.5068e-05\n",
      "Epoch 12/50\n",
      "1000/1000 [==============================] - 164s 164ms/step - loss: 6.2831e-06 - val_loss: 1.2710e-05\n",
      "Epoch 13/50\n",
      "1000/1000 [==============================] - 164s 164ms/step - loss: 5.0428e-06 - val_loss: 7.7934e-06\n",
      "Epoch 14/50\n",
      "1000/1000 [==============================] - 164s 164ms/step - loss: 4.7380e-06 - val_loss: 5.0746e-06\n",
      "Epoch 15/50\n",
      "1000/1000 [==============================] - 164s 164ms/step - loss: 4.3827e-06 - val_loss: 5.2524e-06\n",
      "Epoch 16/50\n",
      "  41/1000 [>.............................] - ETA: 1:49 - loss: 5.3175e-06"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Process ForkPoolWorker-30:\n",
      "Process ForkPoolWorker-32:\n",
      "Process ForkPoolWorker-12:\n",
      "Process ForkPoolWorker-31:\n",
      "Process ForkPoolWorker-18:\n",
      "Process ForkPoolWorker-11:\n",
      "Process ForkPoolWorker-20:\n",
      "Process ForkPoolWorker-24:\n",
      "Process ForkPoolWorker-34:\n",
      "Process ForkPoolWorker-29:\n",
      "Process ForkPoolWorker-26:\n",
      "Process ForkPoolWorker-35:\n",
      "Process ForkPoolWorker-23:\n",
      "Process ForkPoolWorker-27:\n",
      "Process ForkPoolWorker-15:\n",
      "Traceback (most recent call last):\n",
      "Process ForkPoolWorker-21:\n",
      "Process ForkPoolWorker-37:\n",
      "Process ForkPoolWorker-28:\n",
      "Process ForkPoolWorker-16:\n",
      "Process ForkPoolWorker-17:\n",
      "Process ForkPoolWorker-38:\n",
      "Process ForkPoolWorker-33:\n",
      "Process ForkPoolWorker-36:\n",
      "Traceback (most recent call last):\n",
      "Process ForkPoolWorker-9:\n",
      "Process ForkPoolWorker-14:\n",
      "Process ForkPoolWorker-25:\n",
      "Traceback (most recent call last):\n",
      "Process ForkPoolWorker-19:\n",
      "Process ForkPoolWorker-13:\n",
      "Process ForkPoolWorker-40:\n",
      "Traceback (most recent call last):\n",
      "Traceback (most recent call last):\n",
      "Traceback (most recent call last):\n",
      "Traceback (most recent call last):\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "Process ForkPoolWorker-39:\n",
      "Traceback (most recent call last):\n",
      "Traceback (most recent call last):\n",
      "Traceback (most recent call last):\n",
      "Traceback (most recent call last):\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "Traceback (most recent call last):\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "Traceback (most recent call last):\n",
      "Traceback (most recent call last):\n",
      "Traceback (most recent call last):\n",
      "Traceback (most recent call last):\n",
      "Traceback (most recent call last):\n",
      "Traceback (most recent call last):\n",
      "Traceback (most recent call last):\n",
      "Traceback (most recent call last):\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "Traceback (most recent call last):\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "Traceback (most recent call last):\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "Traceback (most recent call last):\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "Traceback (most recent call last):\n",
      "Traceback (most recent call last):\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 108, in worker\n",
      "    task = get()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "Traceback (most recent call last):\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "Traceback (most recent call last):\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 119, in worker\n",
      "    result = (True, func(*args, **kwds))\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "Traceback (most recent call last):\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 119, in worker\n",
      "    result = (True, func(*args, **kwds))\n",
      "Traceback (most recent call last):\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 108, in worker\n",
      "    task = get()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 44, in mapstar\n",
      "    return list(map(*args))\n",
      "Traceback (most recent call last):\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 44, in mapstar\n",
      "    return list(map(*args))\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 119, in worker\n",
      "    result = (True, func(*args, **kwds))\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 108, in worker\n",
      "    task = get()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 119, in worker\n",
      "    result = (True, func(*args, **kwds))\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 125, in worker\n",
      "    put((job, i, result))\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 119, in worker\n",
      "    result = (True, func(*args, **kwds))\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 108, in worker\n",
      "    task = get()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 119, in worker\n",
      "    result = (True, func(*args, **kwds))\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 119, in worker\n",
      "    result = (True, func(*args, **kwds))\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 108, in worker\n",
      "    task = get()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 119, in worker\n",
      "    result = (True, func(*args, **kwds))\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 108, in worker\n",
      "    task = get()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/queues.py\", line 334, in get\n",
      "    with self._rlock:\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 258, in _bootstrap\n",
      "    self.run()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/synchronize.py\", line 96, in __enter__\n",
      "    return self._semlock.__enter__()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 108, in worker\n",
      "    task = get()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/queues.py\", line 334, in get\n",
      "    with self._rlock:\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/queues.py\", line 334, in get\n",
      "    with self._rlock:\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/queues.py\", line 334, in get\n",
      "    with self._rlock:\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 44, in mapstar\n",
      "    return list(map(*args))\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/queues.py\", line 334, in get\n",
      "    with self._rlock:\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 108, in worker\n",
      "    task = get()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 44, in mapstar\n",
      "    return list(map(*args))\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 44, in mapstar\n",
      "    return list(map(*args))\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 108, in worker\n",
      "    task = get()\n",
      "KeyboardInterrupt\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 108, in worker\n",
      "    task = get()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/synchronize.py\", line 96, in __enter__\n",
      "    return self._semlock.__enter__()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/queues.py\", line 334, in get\n",
      "    with self._rlock:\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 108, in worker\n",
      "    task = get()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/synchronize.py\", line 96, in __enter__\n",
      "    return self._semlock.__enter__()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 108, in worker\n",
      "    task = get()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/queues.py\", line 334, in get\n",
      "    with self._rlock:\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/synchronize.py\", line 96, in __enter__\n",
      "    return self._semlock.__enter__()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 108, in worker\n",
      "    task = get()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/queues.py\", line 334, in get\n",
      "    with self._rlock:\n",
      "  File \"/home/dat/Documents/ML-Receiver/radioml/dataset.py\", line 49, in emit_signal\n",
      "    modulated_packet = self.modulator.modulate(encoded_packet)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/queues.py\", line 346, in put\n",
      "    with self._wlock:\n",
      "  File \"/home/dat/Documents/ML-Receiver/radioml/dataset.py\", line 47, in emit_signal\n",
      "    encoded_packet   = cp.channelcoding.conv_encode(packet, self.trellis)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/queues.py\", line 334, in get\n",
      "    with self._rlock:\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 108, in worker\n",
      "    task = get()\n",
      "  File \"/home/dat/Documents/ML-Receiver/radioml/dataset.py\", line 47, in emit_signal\n",
      "    encoded_packet   = cp.channelcoding.conv_encode(packet, self.trellis)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 44, in mapstar\n",
      "    return list(map(*args))\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 108, in worker\n",
      "    task = get()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/queues.py\", line 334, in get\n",
      "    with self._rlock:\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/site-packages/scikit_commpy-0.3.0-py3.6.egg/commpy/channelcoding/convcode.py\", line 346, in conv_encode\n",
      "    current_input = bitarray2dec(inbits[i*k:(i+1)*k])\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 119, in worker\n",
      "    result = (True, func(*args, **kwds))\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/site-packages/scikit_commpy-0.3.0-py3.6.egg/commpy/modulation.py\", line 46, in modulate\n",
      "    baseband_symbols = mapfunc(arange(0, len(input_bits), self.num_bits_symbol))\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/synchronize.py\", line 96, in __enter__\n",
      "    return self._semlock.__enter__()\n",
      "KeyboardInterrupt\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/queues.py\", line 334, in get\n",
      "    with self._rlock:\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/queues.py\", line 334, in get\n",
      "    with self._rlock:\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/process.py\", line 93, in run\n",
      "    self._target(*self._args, **self._kwargs)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 44, in mapstar\n",
      "    return list(map(*args))\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/queues.py\", line 334, in get\n",
      "    with self._rlock:\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/synchronize.py\", line 96, in __enter__\n",
      "    return self._semlock.__enter__()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/synchronize.py\", line 96, in __enter__\n",
      "    return self._semlock.__enter__()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/synchronize.py\", line 96, in __enter__\n",
      "    return self._semlock.__enter__()\n",
      "KeyboardInterrupt\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 44, in mapstar\n",
      "    return list(map(*args))\n",
      "  File \"/home/dat/Documents/ML-Receiver/radioml/dataset.py\", line 47, in emit_signal\n",
      "    encoded_packet   = cp.channelcoding.conv_encode(packet, self.trellis)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 44, in mapstar\n",
      "    return list(map(*args))\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/synchronize.py\", line 96, in __enter__\n",
      "    return self._semlock.__enter__()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/synchronize.py\", line 96, in __enter__\n",
      "    return self._semlock.__enter__()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 108, in worker\n",
      "    task = get()\n",
      "KeyboardInterrupt\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/synchronize.py\", line 96, in __enter__\n",
      "    return self._semlock.__enter__()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 108, in worker\n",
      "    task = get()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/site-packages/scikit_commpy-0.3.0-py3.6.egg/commpy/channelcoding/convcode.py\", line 366, in conv_encode\n",
      "    p_outbits[j] = outbits[i]\n",
      "KeyboardInterrupt\n",
      "KeyboardInterrupt\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/synchronize.py\", line 96, in __enter__\n",
      "    return self._semlock.__enter__()\n",
      "  File \"/home/dat/Documents/ML-Receiver/radioml/dataset.py\", line 47, in emit_signal\n",
      "    encoded_packet   = cp.channelcoding.conv_encode(packet, self.trellis)\n",
      "KeyboardInterrupt\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 108, in worker\n",
      "    task = get()\n",
      "  File \"/home/dat/Documents/ML-Receiver/radioml/dataset.py\", line 49, in emit_signal\n",
      "    modulated_packet = self.modulator.modulate(encoded_packet)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/site-packages/scikit_commpy-0.3.0-py3.6.egg/commpy/utilities.py\", line 69, in bitarray2dec\n",
      "    for i in range(len(in_bitarray)):\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 108, in worker\n",
      "    task = get()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/site-packages/scikit_commpy-0.3.0-py3.6.egg/commpy/channelcoding/convcode.py\", line 348, in conv_encode\n",
      "    outbits[j*n:(j+1)*n] = dec2bitarray(current_output, n)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/synchronize.py\", line 96, in __enter__\n",
      "    return self._semlock.__enter__()\n",
      "  File \"/home/dat/Documents/ML-Receiver/radioml/dataset.py\", line 47, in emit_signal\n",
      "    encoded_packet   = cp.channelcoding.conv_encode(packet, self.trellis)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/queues.py\", line 334, in get\n",
      "    with self._rlock:\n",
      "KeyboardInterrupt\n",
      "KeyboardInterrupt\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/site-packages/scikit_commpy-0.3.0-py3.6.egg/commpy/modulation.py\", line 46, in modulate\n",
      "    baseband_symbols = mapfunc(arange(0, len(input_bits), self.num_bits_symbol))\n",
      "KeyboardInterrupt\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/queues.py\", line 334, in get\n",
      "    with self._rlock:\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/pool.py\", line 108, in worker\n",
      "    task = get()\n",
      "KeyboardInterrupt\n",
      "KeyboardInterrupt\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/site-packages/scikit_commpy-0.3.0-py3.6.egg/commpy/channelcoding/convcode.py\", line 349, in conv_encode\n",
      "    current_state = next_state_table[current_state][current_input]\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/site-packages/scikit_commpy-0.3.0-py3.6.egg/commpy/channelcoding/convcode.py\", line 348, in conv_encode\n",
      "    outbits[j*n:(j+1)*n] = dec2bitarray(current_output, n)\n",
      "KeyboardInterrupt\n",
      "  File \"/home/dat/Documents/ML-Receiver/radioml/dataset.py\", line 47, in emit_signal\n",
      "    encoded_packet   = cp.channelcoding.conv_encode(packet, self.trellis)\n",
      "KeyboardInterrupt\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/queues.py\", line 334, in get\n",
      "    with self._rlock:\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/queues.py\", line 334, in get\n",
      "    with self._rlock:\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/site-packages/scikit_commpy-0.3.0-py3.6.egg/commpy/channelcoding/convcode.py\", line 347, in conv_encode\n",
      "    current_output = output_table[current_state][current_input]\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/queues.py\", line 335, in get\n",
      "    res = self._reader.recv_bytes()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/synchronize.py\", line 96, in __enter__\n",
      "    return self._semlock.__enter__()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/queues.py\", line 334, in get\n",
      "    with self._rlock:\n",
      "KeyboardInterrupt\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/site-packages/numpy/lib/function_base.py\", line 2755, in __call__\n",
      "    return self._vectorize_call(func=func, args=vargs)\n",
      "  File \"/home/dat/Documents/ML-Receiver/radioml/dataset.py\", line 47, in emit_signal\n",
      "    encoded_packet   = cp.channelcoding.conv_encode(packet, self.trellis)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/queues.py\", line 334, in get\n",
      "    with self._rlock:\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/synchronize.py\", line 96, in __enter__\n",
      "    return self._semlock.__enter__()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/synchronize.py\", line 96, in __enter__\n",
      "    return self._semlock.__enter__()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/site-packages/scikit_commpy-0.3.0-py3.6.egg/commpy/channelcoding/convcode.py\", line 348, in conv_encode\n",
      "    outbits[j*n:(j+1)*n] = dec2bitarray(current_output, n)\n",
      "KeyboardInterrupt\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/synchronize.py\", line 96, in __enter__\n",
      "    return self._semlock.__enter__()\n",
      "KeyboardInterrupt\n",
      "KeyboardInterrupt\n",
      "KeyboardInterrupt\n",
      "KeyboardInterrupt\n",
      "KeyboardInterrupt\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/connection.py\", line 216, in recv_bytes\n",
      "    buf = self._recv_bytes(maxlength)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/synchronize.py\", line 96, in __enter__\n",
      "    return self._semlock.__enter__()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/synchronize.py\", line 96, in __enter__\n",
      "    return self._semlock.__enter__()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/site-packages/numpy/lib/function_base.py\", line 2831, in _vectorize_call\n",
      "    outputs = ufunc(*inputs)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "KeyboardInterrupt\n",
      "KeyboardInterrupt\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/synchronize.py\", line 96, in __enter__\n",
      "    return self._semlock.__enter__()\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/site-packages/numpy/lib/function_base.py\", line 2755, in __call__\n",
      "    return self._vectorize_call(func=func, args=vargs)\n",
      "KeyboardInterrupt\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/connection.py\", line 407, in _recv_bytes\n",
      "    buf = self._recv(4)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/site-packages/numpy/lib/function_base.py\", line 2831, in _vectorize_call\n",
      "    outputs = ufunc(*inputs)\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/site-packages/scikit_commpy-0.3.0-py3.6.egg/commpy/modulation.py\", line 44, in <lambda>\n",
      "    self.constellation[bitarray2dec(input_bits[i:i+self.num_bits_symbol])])\n",
      "KeyboardInterrupt\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/site-packages/scikit_commpy-0.3.0-py3.6.egg/commpy/modulation.py\", line 44, in <lambda>\n",
      "    self.constellation[bitarray2dec(input_bits[i:i+self.num_bits_symbol])])\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/site-packages/scikit_commpy-0.3.0-py3.6.egg/commpy/utilities.py\", line 70, in bitarray2dec\n",
      "    number = number + in_bitarray[i]*pow(2, len(in_bitarray)-1-i)\n",
      "KeyboardInterrupt\n",
      "KeyboardInterrupt\n",
      "  File \"/home/dat/miniconda2/envs/deepcom/lib/python3.6/multiprocessing/connection.py\", line 379, in _recv\n",
      "    chunk = read(handle, remaining)\n",
      "KeyboardInterrupt\n",
      "KeyboardInterrupt\n",
      "KeyboardInterrupt\n"
     ]
    }
   ],
   "source": [
    "model.compile('adam','mse')\n",
    "history = model.fit_generator(\n",
    "    generator=training_generator,\n",
    "    validation_data=validation_generator,\n",
    "    steps_per_epoch=1000,\n",
    "    validation_steps=100,\n",
    "    callbacks=[tf.keras.callbacks.ModelCheckpoint('../models/cfo_nn_tanh_omgega.hdf5', save_best_only=True)],\n",
    "    epochs=10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Source: https://machinelearningmastery.com/display-deep-learning-model-training-history-in-keras/\n",
    "plt.plot(history.history['loss'])\n",
    "plt.plot(history.history['val_loss'])\n",
    "plt.title('model loss')\n",
    "plt.ylabel('Loss')\n",
    "plt.xlabel('Epoch')\n",
    "plt.legend(['Training', 'Validation'], loc='upper left')\n",
    "plt.grid()\n",
    "plt.semilogy()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Evaluate models on different Omegas and SNRs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     },
     "base_uri": "https://localhost:8080/",
     "height": 86
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 1192,
     "status": "ok",
     "timestamp": 1532643520537,
     "user": {
      "displayName": "Dat Nguyen",
      "photoUrl": "//lh3.googleusercontent.com/-irIcNYd-KIw/AAAAAAAAAAI/AAAAAAAAAEs/NlM8kG6RL4Q/s50-c-k-no/photo.jpg",
      "userId": "108917076199533451784"
     },
     "user_tz": 420
    },
    "id": "YE1Eid_jeJ20",
    "outputId": "979f2626-d17e-47c4-c9f0-2e268b8d8d59"
   },
   "outputs": [],
   "source": [
    "# Load best trained models\n",
    "feedforward = tf.keras.models.load_model('../models/cfo_nn_tanh_omgega.hdf5')\n",
    "\n",
    "logs = {}\n",
    "for w in [1/20, 1/50, 1/100]:\n",
    "    logs[w] = []\n",
    "    print('[Omega]: %f' % w)\n",
    "    for snr in [10.0, 15.0, 20.0, 25.0]:\n",
    "        # Define testing data\n",
    "        testing_generator = radio.cfo_data_generator(w, snr, batch_size=2000, seed=1111)\n",
    "        test_inputs, test_labels = next(testing_generator)\n",
    "        \n",
    "        # Make Predictions\n",
    "        nn_predictions  = feedforward.predict(test_inputs)\n",
    "        \n",
    "        # Compute MSE\n",
    "        nn_mse = mean_squared_error(test_labels, nn_predictions)\n",
    "        \n",
    "        print('\\t[SNR]: %.2f || Feedforward MSE: %.8f' % (snr, nn_mse))\n",
    "        logs[w].append([nn_mse, rnn_mse])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "k= np.random.randint(0, len(test_labels) - 5)\n",
    "print('K=%d'%k)\n",
    "print(\"True CFO Rate:         \", test_labels[k:k+4])\n",
    "print(\"RNN Estimate CFO Rate: \", rnn_predictions[k:k+4].T)\n",
    "print(\"NN  Estimate CFO Rate: \", nn_predictions[k:k+4].T)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Evaluate on longer preamble"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "radio = RadioData(data_len    =DATA_LEN, \n",
    "                  preamble_len=2*PREAMBLE_LEN,  # 80\n",
    "                  channels_len=CHANNEL_LEN,\n",
    "                  modulation_scheme='QPSK')\n",
    "logs = {}\n",
    "for w in [1/20, 1/50, 1/100]:\n",
    "    logs[w] = []\n",
    "    print('[Omega]: %f' % w)\n",
    "    for snr in [10.0, 15.0, 20.0, 25.0]:\n",
    "        # Define testing data\n",
    "        testing_generator = radio.cfo_correction_data_gen(w, snr, batch_size=2000, seed=1111)\n",
    "        test_inputs, test_labels = next(testing_generator)\n",
    "        \n",
    "        # Make Predictions\n",
    "        rnn_predictions = recurrent_nn.predict(test_inputs)\n",
    "        \n",
    "        # Compute MSE\n",
    "        rnn_mse =mean_squared_error(test_labels, rnn_predictions)\n",
    "        \n",
    "        print('\\t[SNR]: %.2f  RecurrentNN MSE: %.8f'% (snr, rnn_mse))\n",
    "        logs[w].append([nn_mse, rnn_mse])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "accelerator": "GPU",
  "colab": {
   "collapsed_sections": [],
   "default_view": {},
   "name": "Copy of TrainCFONet.ipynb",
   "provenance": [
    {
     "file_id": "14VQfWvCqZQ1Ui16uXdtbU2Hpp3nDHGIu",
     "timestamp": 1532642180478
    }
   ],
   "version": "0.3.2",
   "views": {}
  },
  "kernelspec": {
   "display_name": "Radio ML",
   "language": "python",
   "name": "radioml"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
